package com.xmall.order.service.impl.other;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.xmall.define.base.Result;
import com.xmall.define.base.ResultCode;
import com.xmall.define.dict.*;
import com.xmall.order.define.dict.RefundStatusEnum;
import com.xmall.order.define.dto.OrderRefundDto;
import com.xmall.define.dto.pay.AlipayRefundDto;
import com.xmall.define.dto.pay.AlipayRefundTradeDto;
import com.xmall.define.dto.pay.AlipayTradeDto;
import com.xmall.exception.XmallException;
import com.xmall.order.define.constants.OrderProperties;
import com.xmall.order.define.dict.AfterSaleStatusEnum;
import com.xmall.order.entity.Order;
import com.xmall.order.entity.OrderItem;
import com.xmall.order.entity.OrderRefund;
import com.xmall.order.entity.OrderRefundRetryLog;
import com.xmall.order.service.IOrderItemService;
import com.xmall.order.service.IOrderRefundRetryLogService;
import com.xmall.order.service.IOrderRefundService;
import com.xmall.order.service.IOrderService;
import com.xmall.order.service.other.IOrderTradeService;
import com.xmall.order.service.remote.PayServiceFeign;
import com.xmall.define.request.pay.AlipayRefundRequest;
import com.xmall.define.request.pay.AlipayRequest;
import com.xmall.order.utils.AmountUtils;
import com.xmall.order.utils.OrderStatusUtils;
import com.xmall.utils.XmallUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

@Slf4j
@Service
public class OrderTradeServiceImpl implements IOrderTradeService {

    @Resource
    private OrderProperties orderProperties;

    @Resource
    private IOrderService orderService;

    @Resource
    private PayServiceFeign payServiceFeign;

    @Resource
    private IOrderRefundRetryLogService orderRefundRetryLogService;

    @Resource
    private IOrderRefundService orderRefundService;

    @Resource
    private IOrderItemService orderItemService;

    /**
     * 支付宝手机网站支付
     */
    @Override
    public String aliMobileWapPay(Long orderId, String returnUrl, HttpServletResponse response) {
        Order entity = orderService.getById(orderId);
        XmallUtils.notNull(entity);

        // 检查订单状态
        OrderStatusUtils.beforePayCheck(entity.getOrderStatus());

        // 调取支付接口
        AlipayRequest request = new AlipayRequest();
        request.setOrderId(orderId);
        request.setOutTradeNo(entity.getOrderSn().toString());
        request.setSubject(orderProperties.getPaySubject());
        request.setTotalAmount(entity.getPayAmount().toString());
        request.setReturnUrl(returnUrl);
        try {
            Result<String> payResult = payServiceFeign.mobileWapPay(request);
            if(payResult.callSuccess()) {
                return payResult.getData();
            }else {
                log.error("aliMobileWapPay: failed");
                throw new XmallException(ResultCode.PAY_ALIPAY_MOBILE_WAP_ERROR);
            }
        }catch (Exception e) {
            throw new XmallException(ResultCode.PAY_ALIPAY_MOBILE_WAP_ERROR);
        }
    }

    /**
     * 订单取消前检查
     */
    @Override
    public Boolean shouldExecuteCancel(Long orderId) {
        try {
            Order entity = orderService.getById(orderId);
            // 订单不存在
            if(null == entity) {
                return false;
            }
            // 订单本地状态不符合
            if(!OrderStatusUtils.beforeCancelCheck(entity.getOrderStatus())) {
                return false;
            }

            // 查询远程交易接口
            Result<AlipayTradeDto> tradeResult = payServiceFeign.tradeInfo(entity.getOrderSn().toString(), entity.getTradeNo());
            // 查询到订单，而且订单远程状态不符合
            if(tradeResult.callSuccess() && tradeResult.getData().getExists()
                    && TradeStatusEnum.TRADE_SUCCESS.getValue().equalsIgnoreCase(tradeResult.getData().getTradeStatus())) {
                log.error("order: {}, already trade success", orderId);
                return false;
            }
            return true;
        }catch (Exception e) {
            return false;
        }
    }

    /**
     * 退款
     */
    @Transactional
    @Override
    public void executeRefund(Long refundLogId) {
        OrderRefundRetryLog log = orderRefundRetryLogService.getById(refundLogId);
        XmallUtils.notNull(log);
        OrderRefund refund = orderRefundService.getById(log.getRefundId());
        XmallUtils.notNull(refund);
        Order order = orderService.getById(refund.getOrderId());
        XmallUtils.notNull(order);

        if(orderRefundRetryLogService.complete(log.getId())) {
            if(PayTypeEnum.ALI_PAY.getType() == order.getPayType()) {
                if(!this.hasAlipayRefundTradeInfo(order, log)) {
                    this.alipayRefund(order, log);
                }
            }else if(PayTypeEnum.WE_CHAT.getType() == order.getPayType()) {
                return;
            }else if(PayTypeEnum.BALANCE.getType() == order.getPayType()) {
                return;
            }else {
                return;
            }

            // 设置订单售后状态
            if(null == refund.getOrderItemId()) {
                orderService.setOrderAfterSaleStatus(refund.getOrderId(), AfterSaleStatusEnum.UN_VALID);
                orderService.setOrderItemAfterSaleStatusByOrderId(refund.getOrderId(), AfterSaleStatusEnum.UN_VALID);
            }else {
                orderService.setOrderAfterSaleStatus(refund.getOrderId(), AfterSaleStatusEnum.UN_VALID);
                orderService.setOrderItemAfterSaleStatus(refund.getOrderItemId(), AfterSaleStatusEnum.UN_VALID);
            }

            // 更新退款记录
            OrderRefund updateRefund = new OrderRefund();
            updateRefund.setId(refund.getId());
            updateRefund.setRefundStatus(RefundStatusEnum.FINISH_REFUND.getStatus());
            updateRefund.setRefundTime(new Date());
            orderRefundService.updateById(updateRefund);

            // 更新订单状态
            this.updateOrderRefundStatus(order.getId());
        }
    }

    /**
     * 更新订单状态
     */
    private void updateOrderRefundStatus(Long id) {
        List<Long> orderItemIdList = orderItemService.list(new QueryWrapper<OrderItem>().eq("order_id", id)).stream().map(orderItem -> orderItem.getId()).collect(Collectors.toList());
        List<OrderRefundDto> successRefundList = orderRefundService.listByOrderId(id).stream().filter(orderRefundDto -> RefundStatusEnum.FINISH_REFUND.getStatus() == orderRefundDto.getRefundStatus()).collect(Collectors.toList());

        Boolean isAllRefund;
        BigDecimal totalRefundAmount = BigDecimal.ZERO;

        // 判断部分退款或全部退款
        if(successRefundList.size() == 1 && null == successRefundList.get(0).getOrderItemId()) {
            isAllRefund = true;
        }else {
            isAllRefund = orderItemIdList.stream().allMatch(orderItemId ->
                successRefundList.stream().anyMatch(orderRefundDto -> orderItemId.longValue() == orderRefundDto.getOrderItemId())
            );
        }

        // 计算退款总金额
        if(successRefundList.size() == 1 && null == successRefundList.get(0).getOrderItemId()) {
            totalRefundAmount = successRefundList.get(0).getRefundAmount();
        }else {
            for (OrderRefundDto orderRefundDto : successRefundList) {
                totalRefundAmount = totalRefundAmount.add(orderRefundDto.getRefundAmount());
            }
        }

        // 更新订单
        Order updateOrder = new Order();
        updateOrder.setId(id);
        updateOrder.setOrderStatus(isAllRefund ? OrderStatusEnum.ALL_REFUND.getStatus() : OrderStatusEnum.PART_REFUND.getStatus());
        updateOrder.setRefundAmount(totalRefundAmount);
        orderService.updateById(updateOrder);
    }

    /**
     * 支付宝退款
     */
    private String alipayRefund(Order order, OrderRefundRetryLog orderRefundRetryLog) {
        AlipayRefundRequest request = new AlipayRefundRequest();
        request.setOutTradeNo(order.getOrderSn().toString());
        request.setTradeNo(order.getTradeNo());
        request.setRefundNo(orderRefundRetryLog.getRefundTradeNo());
        request.setRefundAmount(AmountUtils.scaleAmount(orderRefundRetryLog.getRefundAmount()));
        request.setRefundReason(orderProperties.getRefundReason());

        Result<AlipayRefundDto> result = payServiceFeign.refund(request);
        if(null == result || !result.callSuccess()) {
            log.error("refund error");
            throw new XmallException(ResultCode.PAY_ALIPAY_REFUND_ERROR);
        }

        return result.getData().getRefundFee();
    }

    /**
     * 支付宝退款查询
     */
    private Boolean hasAlipayRefundTradeInfo(Order order, OrderRefundRetryLog orderRefundRetryLog) {
        Result<AlipayRefundTradeDto> result = payServiceFeign.refundTradeInfo(order.getOrderSn().toString(), order.getTradeNo(), orderRefundRetryLog.getRefundTradeNo());
        return null != result && result.callSuccess() && "REFUND_SUCCESS".equalsIgnoreCase(result.getData().getRefundStatus());
    }
}
